using System.Collections.Generic;
using UnityEngine;

public class XEventDispatcher : MonoBehaviour
{
	private readonly Dictionary<string, XEventModule> modules = new Dictionary<string, XEventModule>();

	/// <summary>
	/// internal post event, support delay
	/// </summary>
	internal class XPostEvent
    {
		public int		eventId;
		public XEventArgs args;
		public float	delay;
	}

	private readonly List<XPostEvent> postEvents = new List<XPostEvent>();
	
	public virtual XEventModule Load(System.Type type)
    {
		if (!modules.ContainsKey(type.Name))
        {
			GameObject moduleGo = new GameObject(type.Name);
			moduleGo.transform.SetParent(transform, false);

			XEventModule module = (XEventModule)moduleGo.AddComponent(type);
			module.SetDispatcher(this);
			module.Active();

			modules.Add(type.Name, module);
		}

		return modules[type.Name];
    }

	public virtual T Load<T>() where T : XEventModule
    {
		string typeName = typeof(T).Name;
		if (!modules.ContainsKey(typeName))
		{
			GameObject moduleGo = new GameObject(typeName);
			moduleGo.transform.SetParent(transform, false);

			T module = moduleGo.AddComponent<T>();
			module.SetDispatcher(this);
			module.Active();

			modules.Add(typeName, module);
		}

		return (T)modules[typeName];
	}

	public virtual void Unload<T>() where T : XEventModule
    {
		string typeName = typeof(T).Name;
		if (modules.ContainsKey(typeName))
		{
			T module = (T)modules[typeName];
			module.Detive();

			modules.Remove(typeName);
			Destroy(module.gameObject);
		}
	}

	public virtual void Unload(System.Type type)
    {
		if (modules.ContainsKey(type.Name))
		{
			XEventModule module = (XEventModule)modules[type.Name];
			module.Detive();

			modules.Remove(type.Name);
			Destroy(module.gameObject);
		}
	}

    public T Query<T>() where T : XEventModule
    {
        return (T)modules[typeof(T).Name];
    }

	public virtual void DispatchEvent(int nEventId, XEventArgs args)
    {
		List<XEventModule> array = new List<XEventModule>(modules.Values);
		for(int i=0; i<array.Count; i++)
        {
			XEventModule module = array[i];
			if (module.HasEvent(nEventId))
            {
				module.FireEvent(nEventId, args);
			}
        }
    }

	public virtual void FireEvent(int nEventId, XEventArgs args)
    {
		DispatchEvent(nEventId, args);
    }

	public virtual void PostEvent(int nEventId, float delayTime, XEventArgs eventArgs)
    {
		postEvents.Add(new XPostEvent{
			eventId = nEventId, args = eventArgs, delay = delayTime,
		});
    }

	public virtual void PostUpdate(float deltaTime)
    {
		for(int i=0; i<postEvents.Count; i++)
        {
			XPostEvent evt = postEvents[i];
			if (evt.delay <= 0)
            {
				DispatchEvent(evt.eventId, evt.args);

				postEvents.RemoveAt(i);
            } 
			else
            {
				evt.delay -= Mathf.Min(evt.delay, deltaTime);
            }
        }
    }

	protected virtual void Update()
    {
		PostUpdate(Time.deltaTime);
    }
}
